﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EmaMetadata
{
    public class EmaProduct
    {
        public List<EmaIdentity> Identities = new List<EmaIdentity>();
        public List<EmaTitle> Titles = new List<EmaTitle>();        //list of titles for the game (complete, brief, etc) gotten from the xml I believe
        public List<EmaDescription> Descriptions = new List<EmaDescription>();
        public string PrimaryPublisher;
        public string PrimaryDeveloper;
        public List<EmaDeveloper> Developers = new List<EmaDeveloper>();
        public List<EmaPublisher> Publishers = new List<EmaPublisher>();
        public List<EmaRegion> Regions = new List<EmaRegion>();
        public EmaFact Genre;
        public EmaFact EditionType;
        public EmaFact PrimaryProductLine;
        public EmaFact Platform;
        public int MinPlayers;
        public int MaxPlayers;
        public List<EmaGameplay> Players = new List<EmaGameplay>();
        public EmaFact ImageStorageType;
        public string ImageStorageBase;
        public List<EmaImage> Images = new List<EmaImage>();
        public List<EmaPrice> Pricing = new List<EmaPrice>();
        public EmaCharacteristics Characteristics;
        public List<EmaRating> Ratings = new List<EmaRating>();
        public string Extra;

        #region Title functions
        /// <summary>
        /// Retrieve the title object matching these specifications
        /// </summary>
        /// <param name="Type"></param>
        /// <returns></returns>
        public EmaTitle GetTitle(string NS, string Type) //Type will be either, Complete, Medium, Brief, or Sorting, as in the xml file
        {
            if (String.IsNullOrEmpty(NS)) {
                NS = Factory.ENTMERCH_NAMESPACE;
            }
            return (from EmaTitle t in Titles where (t.TitleData.NS == NS) && (t.TitleData.FactName == Type) select t).FirstOrDefault();
            // ^This line picks a specific EmaTitle t from a list(Titles) that fits the appropriate "length slot" and has the correct ns
        }

        public EmaTitle GetTitle(string NS, int id)
        {
            if (String.IsNullOrEmpty(NS)) {
                NS = Factory.ENTMERCH_NAMESPACE;
            }
            return (from EmaTitle t in Titles where (t.TitleData.NS == NS) && (t.TitleData.ID == id) select t).FirstOrDefault();
        }

        /// <summary>
        /// Set a title object matching these specifications
        /// </summary>
        /// <param name="NS"></param>
        /// <param name="Type"></param>
        /// <returns></returns>
        public EmaTitle SetTitle(string NS, string Type, int id, string value)
        {
            // Attempt to retrieve the title
            EmaTitle et = GetTitle(NS, Type);

            // If the title doesn't exist, create a new one
            if (et == null) {
                et = new EmaTitle();
                et.TitleData = new EmaFact() { NS = NS, FactName = Type, ID = id };
                et.Value = value;
                Titles.Add(et);
            } else {
                et.Value = value;
            }
            return et;
        }

        public void RemoveTitle(string NS, int id)
        {
            Titles.Remove(GetTitle(NS, id));
        }

        #endregion

        #region Description functions
        /// <summary>
        /// Set a description object matching these specifications
        /// </summary>
        /// <param name="NS"></param>
        /// <param name="Type"></param>
        /// <returns></returns>
        /// 

        public EmaDescription GetDescription(string NS, string Type) //Type will either be short, long, or requirements, as in the xml file
        {
            if (String.IsNullOrEmpty(NS)) {
                NS = Factory.ENTMERCH_NAMESPACE;
            }
            return (from EmaDescription d in Descriptions where (d.DescriptionData.NS == NS) && (d.DescriptionData.FactName == Type) select d).FirstOrDefault();
        }

        public EmaDescription GetDescription(string NS, int id) //Type will either be short, long, or requirements, as in the xml file
        {
            if (String.IsNullOrEmpty(NS)) {
                NS = Factory.ENTMERCH_NAMESPACE;
            }
            return (from EmaDescription d in Descriptions where (d.DescriptionData.NS == NS) && (d.DescriptionData.ID == id) select d).FirstOrDefault();
        }

        public EmaDescription SetDescription(string NS, string Type, int id, string value)
        {
            // Attempt to retrieve the title
            EmaDescription d = GetDescription(NS, Type);

            // If the title doesn't exist, create a new one
            if (d == null) {
                d = new EmaDescription();
                d.DescriptionData = new EmaFact() { NS = NS, FactName = Type, ID = id };
                d.Value = value;
                Descriptions.Add(d);
            } else {
                d.Value = value;
            }
            return d;
        }

        public void RemoveDescription(string NS, int id)
        {
            Descriptions.Remove(GetDescription(NS, id));
        }

        /// <summary>
        /// Retrieve a description for these specifications
        /// </summary>
        /// <param name="NS"></param>
        /// <param name="Type"></param>
        /// <returns></returns>

        #endregion

        #region Regions (or Release Dates)
        public EmaRegion GetRegion(string country_code) //Type will be a country code, in this case US or CA, There is no NS because CountryCode is a string and not an EmaFact
        {
            return (from EmaRegion r in Regions where (r.CountryCode == country_code) select r).FirstOrDefault(); //Does this need to be FirstOrDefault()? What is the benefit of the "Default"?
        }

        public void SetRegion(string country_code, Nullable<DateTime> rl)
        {
            if (GetRegion(country_code) != null)
            {
                GetRegion(country_code).ReleaseDate = rl;
            }
            else
            {
                EmaRegion r = new EmaRegion();
                r.CountryCode = country_code;
                r.ReleaseDate = rl;
                Regions.Add(r);
            }
        }    

        public void RemoveRegions(string country_code)
        {
            Regions.Remove(GetRegion(country_code));
        }
        #endregion

        #region Platforms
        // May have some reduncancies, but otherwise sets the platform     ~F
        public void SetPlatform(string ns, int id, string platname)
        {
            if (Platform == null) {
                this.Platform = new EmaFact { NS = ns, ID = id, FactName = platname };
            } else {
                Platform.NS = ns;
                Platform.ID = id;
                Platform.FactName = platname;
                // made this section so it wouldn't reinitialize Platform, in case that causes an error in C#    ~F
            }
        }
        #endregion

        #region Identity functions
        /// <summary>
        /// Retrieve the identity object matching these specifications
        /// </summary>
        /// <param name="NS"></param>
        /// <param name="Type"></param>
        /// <returns></returns>
        public EmaIdentity GetIdentity(string NS, string Type)
        {
            if (String.IsNullOrEmpty(NS)) {
                NS = Factory.ENTMERCH_NAMESPACE;
            }
            return (from EmaIdentity i in Identities where (i.IdentityData.NS == NS) && (i.IdentityData.FactName == Type) select i).FirstOrDefault();
        }

        public EmaIdentity GetIdentity(string NS, int id)
        {
            if (String.IsNullOrEmpty(NS)) {
                NS = Factory.ENTMERCH_NAMESPACE;
            }
            return (from EmaIdentity i in Identities where (i.IdentityData.NS == NS) && (i.IdentityData.ID == id) select i).FirstOrDefault();
        }

        public void SetIdentity(string ns, string typename, int id, string value)
        {
            EmaIdentity i = GetIdentity(ns, typename);
            if (i == null) {
                i = new EmaIdentity();
                i.Value = value;
                i.IdentityData = new EmaFact { NS = ns, ID = id, FactName = typename };
                this.Identities.Add(i);
            } else {
                i.Value = value;
            }


        }


        public void RemoveIdentity(string NS, string Type)
        {
            Identities.Remove(GetIdentity(NS, Type));
        }

        public void RemoveIdentity(string NS, int id)
        {
            Identities.Remove(GetIdentity(NS, id));
        }

        #endregion

        #region Gameplay functions


        public EmaGameplay GetPlayers(string NS, string Type) //type is either Online, Local, LAN, or Cooperative   ~F
        {
            if (String.IsNullOrEmpty(NS)) {
                NS = Factory.ENTMERCH_NAMESPACE;
            }
            return (from EmaGameplay g in Players where (g.GameplayData.NS == NS) && (g.GameplayData.FactName == Type) select g).FirstOrDefault();
        }

        public EmaGameplay GetPlayers(string NS, int id) //type is either Online, Local, LAN, or Cooperative   ~F
        {
            if (String.IsNullOrEmpty(NS)) {
                NS = Factory.ENTMERCH_NAMESPACE;
            }
            return (from EmaGameplay g in Players where (g.GameplayData.NS == NS) && (g.GameplayData.ID == id) select g).FirstOrDefault();
        }

        public void SetGameplay(string ns, string typename, int id, int min, int max)
        {
            EmaGameplay g = GetPlayers(ns, typename);
            if (g == null) {
                EmaFact f = new EmaFact() { NS = ns, ID = id, FactName = typename };
                g = new EmaGameplay() { GameplayData = f };
                this.Players.Add(g);
            }
            g.Min = min;
            g.Max = max;
        }

        public void RemoveGameplay(string ns, int id)
        {
            EmaGameplay g = GetPlayers(ns, id);
            if (g != null) {
                Players.Remove(g);
            }
        }



        /// <summary>
        /// Convenience function to set the players counts using strings
        /// </summary>
        /// <param name="NS"></param>
        /// <param name="Type"></param>
        /// <param name="id"></param>
        /// <param name="str_min"></param>
        /// <param name="str_max"></param>
        /// <returns></returns>

        #endregion

        #region Pricing functions // Needs embellishment    ~F
        /* 
         * The Price has nothing about it in the lookupcodes.csv, so I don't know how to handle the EmaFact Pricedata inside the EmaPrice(s)    1/31/2013   ~F
         * 
         */

        public EmaPrice GetPrice(string NS, string Type, int id, string countryCode, Nullable<DateTime> date)
        {
            if (String.IsNullOrEmpty(NS)) {
                NS = Factory.ENTMERCH_NAMESPACE;
            }
            return (from EmaPrice t in Pricing where (t.PriceData.NS == NS) 
                        && (t.PriceData.ID == id) 
                        && (t.CountryCode == countryCode) 
                        && (MatchDate(t.EffectiveDate,date)) select t).FirstOrDefault();
        }

        private bool MatchDate(DateTime? date1, DateTime? date2)
        {
            // Ok if both null
            if (date1 == null && date2 == null) return true;

            // If both non-null, match dates
            if (date1 != null && date2 != null) {
                return date1.Value.Year == date2.Value.Year &&
                    date1.Value.Month == date2.Value.Month &&
                    date1.Value.Day == date2.Value.Day;
            }

            // No match
            return false;
        }

        public EmaPrice SetPrice(string NS, string Type, int id, string countryCode, string currency, decimal value, Nullable<DateTime> effectiveDate)
        {
            // Attempt to retrieve the Price
            EmaPrice et = GetPrice(NS, Type, id, countryCode, effectiveDate);

            // If the Price doesn't exist, create a new one
            if (et == null) {
                et = new EmaPrice();
                et.PriceData = new EmaFact() { NS = NS, FactName = Type, ID = id };
                et.CountryCode = countryCode;
                et.Currency = currency;
                et.EffectiveDate = effectiveDate;
                et.Value = value;
                Pricing.Add(et);
            } else {
                et.Value = value;
                et.CountryCode = countryCode;
                et.Currency = currency;
                et.EffectiveDate = effectiveDate;
                et.Value = value;
            }
            return et;
        }

        public void RemovePrice(string NS, string Type, int ID, string countryCode, Nullable<DateTime> effectiveDate)
        {
            Pricing.Remove(GetPrice(NS, Type, ID, countryCode, effectiveDate));
        }

        #endregion

        #region Rating functions  //Has yet to implement descriptors ie Blooad & Gore, Mild violence, etc    ~F
        public EmaRating GetRating(string NS, int ID)
        {
            if (String.IsNullOrEmpty(NS)) {
                NS = Factory.ENTMERCH_NAMESPACE;
            }
            return (from EmaRating t in Ratings where (t.AgencyData.NS == NS) && (t.AgencyData.ID == ID) select t).FirstOrDefault();
        }

        public EmaRating SetRating(string NS, string agencyName, int id, string rating) //If
        {
            // Attempt to retrieve the Rating
            EmaRating et = GetRating(NS, id);

            // If the Rating doesn't exist, create a new one
            if (et == null) {
                et = new EmaRating();
                et.AgencyData = new EmaFact() { NS = NS, FactName = agencyName, ID = id };
                et.Rating = rating;
                Ratings.Add(et);
            } else {
                et.Rating = rating;
                et.AgencyData.ID = id; // the ID changes with the rating in this circumstance. Ie ESRB E and ESRB M have different ids. 
            }
            return et;
        }

        public void RemoveRating(string NS, int ID)
        {
            Ratings.Remove(GetRating(NS, ID));
        }

        #endregion

        #region Publisher functions
        public EmaPublisher GetPublisher(int relevance)
        {
            return (from EmaPublisher p in Publishers where (p.Relevance == relevance) select p).FirstOrDefault();
        }

        public EmaPublisher SetPublisher(int relevance, string name)
        {
            EmaPublisher et = GetPublisher(relevance);

            // If the Publisher doesn't exist, create a new one
            if (et == null) {
                et = new EmaPublisher();
                et.Name = name;
                Publishers.Add(et);
            } else {
                et.Name = name;
            }
            return et;
        }

        public void RemovePublisher(int relevance)
        {
            Publishers.Remove(GetPublisher(relevance));
        }

        #endregion

        #region Image functions





        #endregion
    }
}
